/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_KNIFE_C_IMAGEKNIFE_DISPATCHER_H
#define IMAGE_KNIFE_C_IMAGEKNIFE_DISPATCHER_H
#include <unordered_map>
#include <list>

#include "request/imageknife_request_internal.h"
#include "interceptor.h"
#include "queue/ijob_queue.h"
#include "queue/default_job_queue.h"
#include "imageknife_task_internal.h"
#include "imageknife_node_internal.h"

namespace ImageKnifePro {

class ImageKnifeDispatcher {
public:
    void Enqueue(std::shared_ptr<ImageKnifeRequestInternal> request);
    bool EnqueueMainSrc(std::shared_ptr<ImageKnifeRequestInternal> request);

    void CancelRequest(std::shared_ptr<ImageKnifeRequestInternal> request);

    std::shared_ptr<ImageData> LoadFromImageSourceSync(ImageSource *imageSource, Context context,
                                                       std::string memoryKey, std::string fileKey);

    void ProcessFrameModeDecoding(std::shared_ptr<ImageKnifeTaskInternal> task);
    void OnTaskComplete(std::shared_ptr<ImageKnifeTaskInternal> task, IJobQueue::EndPhase phase);
    void ProcessRemainingTask(std::shared_ptr<ImageKnifeTaskInternal> task);
    int maxRequests = 8;

    static ImageKnifeDispatcher &GetInstance();
private:
    class JobListsMap {
    public:
        size_t Size();
        // return true 表示需要立即执行job
        bool Emplace(const std::string &key, IJobQueue::Job &&job);
        std::shared_ptr<std::list<IJobQueue::Job>> Move(const std::string &key);
    private:
        std::mutex lock_;
        std::unordered_map<std::string, std::shared_ptr<std::list<IJobQueue::Job>>> jobListsMap_;
    };

    JobListsMap loadingJobMap_;
    JobListsMap decodingJobMap_;
    IJobQueue *jobQueuePtr_ = new DefaultJobQueue();

    ImageKnifeDispatcher();
    ~ImageKnifeDispatcher();
    ImageKnifeDispatcher(const ImageKnifeDispatcher&) = delete;
    ImageKnifeDispatcher &operator = (const ImageKnifeDispatcher&) = delete;

    typedef std::list<std::function<void(const std::shared_ptr<ImageKnifeTaskInternal> &task)>> FuncList;
    void OnJobFailed(const IJobQueue::Job &job, FuncList *funcList);
    void OnJobSuccess(const IJobQueue::Job &job, FuncList *funcList, const IJobQueue::EndPhase &phase,
                      bool &writeMemory, std::shared_ptr<ImageKnifeTaskInternal> &task);
    void CheckFileCacheRequirement(const IJobQueue::Job &job, bool &needWriteFileCache,
                                   std::list<std::shared_ptr<ImageKnifeRequestInternal>> &requestList);
    void WriteFileCacheAndResolvePromise(const std::shared_ptr<ImageKnifeTaskInternal> &task,
        const std::shared_ptr<std::list<IJobQueue::Job>> &jobList, const bool &writeFileCache,
        std::list<std::shared_ptr<ImageKnifeRequestInternal>> &requestList);
    void WriteFileCacheFromSyncLoad(const std::shared_ptr<ImageKnifeTaskInternal> &task,
                                    const std::shared_ptr<ImageKnifeOption> &option);
    void RequeueCanceledJob(IJobQueue::Job &job);
    bool ExecuteJob(IJobQueue::Job);

    bool ExecuteTask(std::shared_ptr<ImageKnifeTaskInternal> task);

    bool LoadSrcFromMemory(IJobQueue::Job);

    // 图片源是本地pixelmap返回true，否则返回false
    bool LoadFromPixelmap(std::shared_ptr<ImageKnifeTaskInternal> task);

    IJobQueue::Job GenerateJob(std::shared_ptr<ImageKnifeRequestInternal> request, ImageRequestType type);

    void LoadImageSource(std::shared_ptr<ImageKnifeTaskInternal> task);
    void LoadImageSourceSync(std::shared_ptr<ImageKnifeTaskInternal> &task);

    void LoadErrorSrc(std::shared_ptr<ImageKnifeRequestInternal> request);

    void CompleteSyncLoadTask(std::shared_ptr<ImageKnifeTaskInternal> task, IJobQueue::EndPhase phase);
    void CompleteJobList(std::shared_ptr<std::list<IJobQueue::Job>> jobList, IJobQueue::EndPhase phase,
                         std::shared_ptr<ImageKnifeTaskInternal> task);
    void PrepareDecodeTask(std::shared_ptr<ImageKnifeTaskInternal> task);

    bool ProcessImageDecodeTransform(std::shared_ptr<ImageKnifeTaskInternal> task);

    bool ProcessImageLoading(std::shared_ptr<ImageKnifeTaskInternal> task);

    void ProcessFileCache(std::shared_ptr<ImageKnifeTaskInternal> task);

    void ProcessCanceledRequest(std::shared_ptr<ImageKnifeRequestInternal> request, ImageRequestType type,
                                std::string info, bool fromQueue = false);

    void ProcessSucceedRequest(std::shared_ptr<ImageKnifeRequestInternal> request, ImageRequestType type,
                               std::shared_ptr<ImageKnifeTaskInternal> task);

    // 在js主线程执行失败的Request，不是task加载中产生的错误，则使用该函数
    void ProcessFailedRequest(std::shared_ptr<ImageKnifeRequestInternal> request, ImageRequestType type,
                              std::string errorInfo);

    // 在加载过程中失败的【主图】Request使用该函数，会进行错误图加载
    void ProcessMainSrcFailedRequest(std::shared_ptr<ImageKnifeRequestInternal> request,
                                     std::shared_ptr<ImageKnifeTaskInternal> task);

    bool IsRequestOverdue(std::shared_ptr<ImageKnifeRequestInternal> request);

    bool ProduceImageCache(std::shared_ptr<ImageKnifeTaskInternal> task);

    bool DisplayImage(std::shared_ptr<ImageKnifeRequestInternal> request, ImageRequestType type,
                      std::shared_ptr<ImageDataCache> imageData);

    // 为Image组件使用的base64字符串前面添加了上prefix： "data:image/xxx;base64," xxx为图片格式
    bool EncodeSourceBase64(std::shared_ptr<ImageKnifeTaskInternal> task);

    void TransformImage(std::shared_ptr<ImageKnifeTaskInternal> task);

    void DispatchNextJob();

    void CancelInterceptor(std::shared_ptr<ImageKnifeRequestInternal> request, ImageRequestType type);

    std::shared_ptr<ImageKnifeNodeInternal> GetImageKnifeNode(std::shared_ptr<ImageKnifeRequestInternal> request);
};

}

#endif // IMAGE_KNIFE_C_IMAGEKNIFE_DISPATCHER_H
